/*
 * ! ******************************************************************************
 *
 * Pentaho Data Integration
 *
 * Copyright (C) 2002-2017 by Hitachi Vantara : http://www.pentaho.com
 *
 *******************************************************************************
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 *
 ******************************************************************************/

package org.pentaho.di.version;

import cn.hutool.core.lang.Console;
import org.apache.commons.lang.StringUtils;
import org.pentaho.di.core.Const;
import org.pentaho.di.core.exception.KettleVersionException;
import org.pentaho.di.core.row.ValueMeta;
import org.pentaho.di.core.xml.XMLHandler;
import org.yzbdl.lanius.version.SliptUtils;

import java.io.UnsupportedEncodingException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.jar.Attributes;
import java.util.jar.Manifest;

/**
 * Singleton class to allow us to see on which date & time the kettle3.jar was built.
 *
 * @author Matt
 * @since 2006-aug-12
 */
public class BuildVersion {
    public static final String REFERENCE_FILE = "/kettle-steps.xml";

    public static final String JAR_BUILD_DATE_FORMAT = "yyyy-MM-dd HH.mm.ss";

    public static final String KETTLE_BUILD_VERSION = "KETTLE_BUILD_VERSION";

    public static final String KETTLE_BUILD_REVISION = "KETTLE_BUILD_REVISION";

    public static final String KETTLE_BUILD_DATE = "KETTLE_BUILD_DATE";

    public static final String KETTLE_BUILD_USER = "KETTLE_BUILD_USER";

    public static final String SPECIFICATION_TITLE = "SPECIFICATION_TITLE";

    public static final String IMPLEMENTATION_VENDOR = "IMPLEMENTATION_VENDOR";

    public static final String IMPLEMENTATION_URL = "IMPLEMENTATION_URL";

    protected static ManifestGetter manifestGetter = new ManifestGetter();

    protected static EnvironmentVariableGetter environmentVariableGetter = new EnvironmentVariableGetter();

    private static BuildVersion instance = null;

    /**
     * @return the instance of the BuildVersion singleton
     */
    public static final BuildVersion getInstance() {
        if (BuildVersion.instance == null) {
            BuildVersion.instance = new BuildVersion();
        }
        return BuildVersion.instance;
    }

    protected static void refreshInstance() {
        BuildVersion.instance = new BuildVersion();
    }

    private String url;
    private String vendor;
    private String productName;
    private String version;
    private String revision;
    private String buildDate;
    private String buildUser;
    private String scheduleName;
    private String licenseTo;

    private void loadBuildInfoFromManifest() throws Exception {
        Manifest manifest = BuildVersion.manifestGetter.getManifest();
        vendor = manifest.getMainAttributes().getValue(Attributes.Name.IMPLEMENTATION_VENDOR);
        productName = manifest.getMainAttributes().getValue(Attributes.Name.SPECIFICATION_TITLE);
        version = manifest.getMainAttributes().getValue(Attributes.Name.IMPLEMENTATION_VERSION);
        revision = manifest.getMainAttributes().getValue(Attributes.Name.SPECIFICATION_VERSION);
        url = manifest.getMainAttributes().getValue("Implementation-URL");
        buildDate = manifest.getMainAttributes().getValue("Compile-Timestamp");
        buildUser = manifest.getMainAttributes().getValue("Compile-User");
        if (version == null) {
            throw new Exception("Error:  Version can't be NULL in manifest.");
        }
    }

    private void loadBuildInfoFromEnvironmentVariables() throws Exception {
        version = BuildVersion.environmentVariableGetter.getEnvVarible("KETTLE_BUILD_VERSION");
        revision = BuildVersion.environmentVariableGetter.getEnvVarible("KETTLE_BUILD_REVISION");
        buildDate = BuildVersion.environmentVariableGetter.getEnvVarible("KETTLE_BUILD_DATE");
        buildUser = BuildVersion.environmentVariableGetter.getEnvVarible("KETTLE_BUILD_USER");
        if (version == null) {
            throw new Exception("Error : Version can't be null in environment variables");
        }
    }

    private BuildVersion() {
        try {
            loadBuildInfoFromManifest();
        } catch (Throwable e) {
            try {
                loadBuildInfoFromEnvironmentVariables();
            } catch (Throwable e2) {
                version = "Unknown";
                revision = "0";
                buildDate = XMLHandler.date2string(new Date());
                buildUser = System.getProperty("user.name");
                productName = Const.PRODUCT_NAME;
            }
        }
    }

    /**
     * @return the buildDate
     */
    public String getBuildDate() {
        return buildDate;
    }

    public Date getBuildDateAsLocalDate() {

        SimpleDateFormat sdf = new SimpleDateFormat(BuildVersion.JAR_BUILD_DATE_FORMAT);
        try {
            Date d = sdf.parse(buildDate);
            return d;
            // ignore failure, retry using standard format
        } catch (ParseException e) {
            // Ignore
        }

        sdf = new SimpleDateFormat(ValueMeta.DEFAULT_DATE_FORMAT_MASK);
        try {
            Date d = sdf.parse(buildDate);
            return d;
            // ignore failure and return null
        } catch (ParseException e) {
            // Ignore
        }

        return null;

    }

    /**
     * @param buildDate the buildDate to set
     */
    public void setBuildDate(String buildDate) throws KettleVersionException {
        // don't let them set a bogus date...
        String tempDate = this.buildDate;
        this.buildDate = buildDate;
        Date testDate = getBuildDateAsLocalDate();
        if (testDate == null) {
            // reset it to the old date...
            this.buildDate = tempDate;
            throw new KettleVersionException("Error:  Invalid date being set as build date"); // this should be
            // localizable... next
            // pass....
        }
    }

    /**
     * @return the version
     */
    public String getVersion() {
        return version;
    }

    /**
     * @param revision the version to set
     */
    public void setVersion(String version) {
        this.version = version;
    }

    /**
     * @return the revision
     */
    public String getRevision() {
        return revision;
    }

    /**
     * @param revision the revision to set
     */
    public void setRevision(String revision) {
        this.revision = revision;
    }

    /**
     * @return the buildUser
     */
    public String getBuildUser() {
        return buildUser;
    }

    /**
     * @param buildUser the buildUser to set
     */
    public void setBuildUser(String buildUser) {
        this.buildUser = buildUser;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getVendor() {
        return vendor;
    }

    public void setVendor(String vendor) {
        this.vendor = vendor;
    }

    public String getProductName() {
        return productName;
    }

    public void setProductName(String productName) {
        this.productName = productName;
    }

    public String getScheduleName() {
        return StringUtils.isNotBlank(scheduleName) ? scheduleName : Const.getCommunitySchedule();
    }

    public void setScheduleName(String scheduleName) {
        this.scheduleName = scheduleName;
    }

    public String getLicenseTo() {
        return licenseTo;
    }

    public void setLicenseTo(String licenseTo) {
        this.licenseTo = licenseTo;
    }

    /**
     * 输出客户端启动信息
     */
    public void showInfoToConsole() throws UnsupportedEncodingException {

        Console.log("  _                        _               \n" +
                " | |                      (_)              \n" +
                " | |        __ _   _ __    _   _   _   ___ \n" +
                " | |       / _` | | '_ \\  | | | | | | / __|\n" +
                " | |____  | (_| | | | | | | | | |_| | \\__ \\\n" +
                " |______|  \\__,_| |_| |_| |_|  \\__,_| |___/");

//        Console.log("********************************************************************************");
        Console.log("================================================================================");
        Date buildDate = getBuildDateAsLocalDate();
        List<String> infoArr = new ArrayList<>();
        // infoArr.add("产品名称: " + getProductName());
        infoArr.add("构建版本: " + getRevision());
        infoArr.add("构建日期: " + String.format("%tY年%tm月%td日", buildDate, buildDate, buildDate));
        // infoArr.add("构建作者: " + getBuildUser());
        infoArr.add("发行单位: " + getVendor());
        // if (StringUtils.isNotBlank(getLicenseTo())) {
        //    infoArr.add("授权单位: " + getLicenseTo());
        // }
        // infoArr.add("官方网址: " + getUrl());
        Integer maxLength = 72;
        String outStart = "=== ";
        String outEnd = " ===";
        for (String str : infoArr) {
            int strLen = 0;
            try {
                strLen = new String(str.getBytes("GBK"), "iso-8859-1").length();
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            if (strLen > maxLength) {
                List<String> list = SliptUtils.chineseSplitFunction(str, maxLength);
                for (int i = 0; i < list.size(); i++) {
                    String out = list.get(i);
                    out = String.format("%-72s", new String(out.getBytes("GBK"), "iso-8859-1"));
                    Console.log(outStart + new String(out.getBytes("ISO-8859-1"), "GBK") + outEnd);
                }
            } else {
                str = String.format("%-72s", new String(str.getBytes("GBK"), "iso-8859-1"));
                Console.log(outStart + new String(str.getBytes("ISO-8859-1"), "GBK") + outEnd);
            }
        }
        Console.log("================================================================================");
    }

}
